#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

#include <sys/ioctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <iostream>
#include<fstream>
#include<string>
#include<exception>
#include<vector>
#include "FileUtilities.h"

using namespace std;
using namespace utilities;

#define QLEN		128
#define	INTERVAL	5	/* secs */
#define BUFFER_SIZE	4096 //data unit size the edge server is supposed to receive
#define FILENAME_SIZE 256


const char* CMD_ACCEPT = "cmd_accept";
const char* ACK_ACCEPT = "ack_accept";
const char* CMD_READY = "cmd__ready";
const char* CMD_SUCCESS = "cmdsuccess";
const int CMD_LENGTH = 10;

const char* CMD_DELETE="cmd_delete";
const char* ACK_DELETE="ack_delete";


int		errexit(const char *format, ...);
void	* TCPProcessRequest(int fd);
void 	* DeleteFileFolder(int fd);

int 	passiveTCP(const char *service, int qlen);
char *loadPAth;

//Function to split the "," separated values to string array
void split(string& s, char c,vector<string>& v)
{
    string::size_type i = 0;
    string::size_type j = s.find(c);
    while (j != string::npos)
    {
        v.push_back(s.substr(i, j-i));
        i = ++j;
        j = s.find(c, j);
        if (j == string::npos)
            v.push_back(s.substr(i, s.length( )));
    }
}


int createFolderStructure(vector<string>& v, int size,string & sPath)
{
 for(int i=0;i<size;i++)
 	{
 		if(v[i].empty())
 			continue;
	    sPath.append(v[i]);
 		int retCode=mkdir(sPath.c_str(),755);
 		sPath.append("/");

 	}
	return 0;
}


int main(int argc, char *argv[])
{


	pthread_t th_ProcessReq;
	pthread_attr_t ta_ProcessReq;

	pthread_t th_DelFile;
	pthread_attr_t ta_DelFile;


	struct	sockaddr_in fsin;	/* the from address of a client	*/
	char	*service = "8080";	/* default port number	*/
	//char *loadPAth;			//loadPAth is now a global variable
	int	msock, ssock,num_bytes;		/* master & slave sockets	*/
	unsigned int	alen;		/* from-address length		*/
	pthread_t	th;
	pthread_attr_t	ta;
	switch (argc)
	 {
	case	1:
		cout<<"trig1"<<endl;
		break;
	case	2:
		service = argv[1];
		break;
	case    3:
		service = argv[1];
		loadPAth=argv[2];
		break;
	default:
		errexit("usage: TCPConcurrentMTd [port]\n");
	}

	//cerr<<"Edge server port is :"<<service<<endl;
	struct stat structStat;


	if(stat(loadPAth, &structStat)==0)
	{
	 cout << "Permissions for this user is :";
	  if( (structStat.st_mode & S_IRUSR) && (structStat.st_mode & S_IWUSR))
	 	cout<<"USer has both read and write permission"<<endl;
	  else
	  {
		  cout<<"No write permissions to folder path :"<<loadPAth<<endl;
		  exit(1);

	  }

	}
	else
	{
		vector<string> vecBasePath;

		string sloadPath(loadPAth);
		split(sloadPath,'/',vecBasePath);

		string strCreatePath;
		createFolderStructure(vecBasePath,vecBasePath.size(),strCreatePath);
	}


	(void) pthread_attr_init(&ta_ProcessReq);
	(void) pthread_attr_setdetachstate(&ta_ProcessReq, PTHREAD_CREATE_DETACHED);

	(void) pthread_attr_init(&ta_DelFile);
	(void) pthread_attr_setdetachstate(&ta_DelFile, PTHREAD_CREATE_DETACHED);

	msock = passiveTCP(service, QLEN);


	while (1)
	{

		alen = sizeof(fsin);
		ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
		if (ssock < 0) {
			if (errno == EINTR)
				continue;
			errexit("accept: %s\n", strerror(errno));
		}
		//cout<<"Entered this area1111"<<endl;
		//cerr<<"ssock value is "<<ssock<<endl;
		char 	*cmd;
		cmd=(char *)malloc(CMD_LENGTH);
		//READ COMMAND  from the server , based on the command invoke particular thread function
		//num_bytes = read(ssock, cmd, 10);
		//int flag=1;
		//setsockopt(ssock,IPPROTO_TCP,TCP_NODELAY,(char *)&flag,sizeof(int));
		num_bytes = recv(ssock, cmd, 10,0);
		//flag=0;
		//setsockopt(ssock,IPPROTO_TCP,TCP_NODELAY,(char *)&flag,sizeof(int));



		//cerr<<"trig1"<<endl;
		if(num_bytes < 0)
		{
		printf("Error reading from client %s %d\n", cmd, num_bytes);
		return -1;
		}
		cerr<<"success reading from the client :"<<cmd<<"and bytes received is :"<<num_bytes<<endl;
		//printf("Success reading from client %s %d\n", cmd, num_bytes);

		cmd[num_bytes] = '\0';

		//cerr<<"Entered this area222"<<endl;

		//const char *strAcceptFile="acceptfile";

		if(!strcmp(cmd,CMD_ACCEPT))
		{
			//TCPProcessRequest(ssock,loadPAth);

			if(pthread_create(&th_ProcessReq,&ta_ProcessReq,(void * (*)(void *))TCPProcessRequest,(void*) ssock)<0)
			    errexit("pthread create for TCPProcessRequest: %s",strerror(errno));

		}
		else if(!strcmp(cmd,CMD_DELETE))
		{

			//DeleteFileFolder(ssock,loadPAth);

			if(pthread_create(&th_DelFile,&ta_DelFile,(void * (*)(void *))DeleteFileFolder,(void*) ssock)<0)
			        errexit("pthread create for DeleteFileFolder : %s",strerror(errno));
		}

		//(void) close(ssock);

		/*if (pthread_create(&th, &ta, (void * (*)(void *))TCPProcessRequest, (void *)ssock) < 0)
			errexit("pthread_create: %s\n", strerror(errno));
		}
		else
		printf("Unrecognised command from Main Server\n");
		//ADD ELSe if structure based on different tasks to be performed by Edge servers		 */


	}

}



int createFolder(string & sPath)
{

 		int retCode=mkdir(sPath.c_str(),755);
 		//if (retCode!=0)
 		//return -1;
	return 0;
}


//void TCPProcessRequest(int fd,char * loadPath)

void *TCPProcessRequest(int fd)
{
	int	num_read_bytes=-1,num_bytes_fname=-1, status;	// number of bytes received and integer from client
	char 	*fname;		// file name
	char 	*fullFilePath;	// file name
	char 	buf[BUFFER_SIZE];// buffer for storing bytes from file we have to decide dynamic size of this buffer


	//char * cmdACK="cmdreceived";
	//(void) write(fd,cmdACK,strlen(cmdACK));
	//const char* ACK_ACCEPT = "ack_accept";
	//cerr<<"Trig from TCPProcessRequest()"<<endl;
	//SEND ACK_ACCEPT
	(void) send(fd,ACK_ACCEPT,10,0);



	//cout<<"Entered this area"<<endl;
	// Read Comma separated string ( vendor_id,file Path(including file name,chunk id ) from client
	fname = (char *)malloc(FILENAME_SIZE);
	//while( (num_bytes_fname = read(fd, fname, FILENAME_SIZE)) < 0);
	//num_bytes_fname = recv(fd, fname, FILENAME_SIZE,0);



	//ofstream oFileNameSt(completeFilePAth, ios::out|ios::binary);

	size_t fileSize=FILENAME_SIZE;
	size_t returnBuffersize=0;
	size_t temppos= returnBuffersize;
	char * ActualBuffer=new char [FILENAME_SIZE];

	while((num_bytes_fname = recv(fd, fname, FILENAME_SIZE,0)) > 0)
		 {
			temppos= returnBuffersize;
			returnBuffersize+=num_bytes_fname;

			ActualBuffer =(char *)realloc(ActualBuffer,returnBuffersize);
			memcpy(ActualBuffer+temppos,fname,num_bytes_fname);
			cout<<"num_bytes_fname = "<<num_bytes_fname<<endl;

			if (num_bytes_fname < FILENAME_SIZE)
				break;
		}



	//zodnum_bytes_fname = read(fd, fname, FILENAME_SIZE);
	if(num_bytes_fname < 0)
	{
		printf("Error reading from client %s %d\n", fname, num_bytes_fname);
		exit(1);
	}
	fname[returnBuffersize] = '\0';

	if(errno==0)
	{
		cout<<"Ready message sent"<<endl;
	// SEND_SUCCESS
	(void) send(fd,CMD_SUCCESS,10,0);

	}
	else
	{
		cout<<"Terminating application as File Metadata to DELETE  is not received completely"<<endl;

		exit(1);
	}

	cout<<"FPath read is :"<<fname<<endl;
	//vector used to store strings vendor id, filepath and chunk index
	vector<string> vecMetaData;
	const int noMetaData=3;

	//vector used to store directory hierarchy with last index as file name
	vector<string> vecFileHierarchy;

	//split the comma separated vendor id, file path and chunk index
	if(fname!=NULL)
	{
		std::string str(fname);
		split(str,',',vecMetaData);
	}

	int lenVecMetaData=vecMetaData.size();
	int	lenVecHierarchy=0;

	string strFileName;
	string vendorID;
	int iChunkIndex=-1;
	//If the data read includes vendorid, filepath and chunk index
	bool bCreateFolderStructure = true;
	if(lenVecMetaData==noMetaData)
	{
		string  strPath=vecMetaData[1];
				//cout<<"strPath = "<<strPath<<endl;
				split(strPath,'/',vecFileHierarchy);

				//cout<<"vecFileHierarchy.size() = "<<vecFileHierarchy.size()<<endl;

				if (vecFileHierarchy.size() > 0) {

					lenVecHierarchy=vecFileHierarchy.size();
					strFileName=vecFileHierarchy[lenVecHierarchy-1];
				}
				else
				{
					bCreateFolderStructure = false;
					strFileName = strPath;
				}

				cout<<"strFileName = "<<strFileName<<endl;


	}
	else
	{

		cerr<<"Unable to extract vendor ID, file name and chunk index from main server "<<endl;
		exit(1);
	}
	string strChunkIndex=vecMetaData[lenVecMetaData-1];
	iChunkIndex= atoi(strChunkIndex.c_str());
	vendorID=vecMetaData[0];
	vendorID.append("/");
	string strLoadPAth(loadPAth);
	strLoadPAth.append("/");
	strLoadPAth.append(vecMetaData[0]);

	//cout<<"VEndor id creation path is :"<<strLoadPAth<<endl;


	struct stat st;
	if(stat(strLoadPAth.c_str(),&st) == 0)
	{
	      // cerr<<"vendor path is [present"<<endl;
	}
	else
	{
	//create directory with vectorid and then pass the vector
		int result= createFolder(strLoadPAth);
		if(!result)
			cout<<"Vendor ID folder  created successfully"<<endl;
		else
			cout<<"Unable to create Vendor ID folder"<<endl;
	}

	strLoadPAth.append("/");

	if (bCreateFolderStructure)
	{

		int resultCode=createFolderStructure(vecFileHierarchy,lenVecHierarchy-1,strLoadPAth);
		if(!resultCode)
			cout<<"File structure created successfully"<<endl;
		else
			cout<<"Unable to create file hierarchy"<<endl;

	}
	//strLoadPAth.append("/");

	//cout<<"complete File Folder path is :"<<strLoadPAth<<endl;

	strLoadPAth.append(strFileName);
	strLoadPAth.append("_");
	strLoadPAth.append(strChunkIndex);

	char *completeFilePAth=(char *)strLoadPAth.c_str();

	cout<<"complete File path is :"<<completeFilePAth<<endl;

	// 1) Do we send an ACK to the other server after file name is read before accepting the file contents
	//An output binary stream writer to write the file to local folder (  2) To determine the file path)

	//ofstream fl(strFileName, ios::out| ios::binary);

	FileUtilities* fu = new FileUtilities();

	char *contentRequest="sendContent";
	//cout<<"contentRequest = "<<contentRequest<<endl;
	//cout<<"strlen(contentRequest) = "<<strlen(contentRequest)<<endl;

	//const char* CMD_READY = "cmd__ready";
	//(void) write(fd,contentRequest,strlen(contentRequest));
	(void) send(fd,CMD_READY,10,0);

	//cout<<"findal part"<<endl;

	int byterEreceoved=-1;
	/*while( (byterEreceoved = read(fd, buf, BUFFER_SIZE)) < 0);
			//zod buf[byterEreceoved]='\0';
	cout<<endl<<"Data received is :"<<buf<<endl;
	fu->writeBytes(completeFilePAth, buf, byterEreceoved);*/

	ofstream oFL(completeFilePAth, ios::out|ios::binary);


	while((byterEreceoved = recv(fd, buf, BUFFER_SIZE,0))  > 0)
	 {
		//cout<<"byterEreceoved = "<<byterEreceoved<<endl;
		fu->writeBytes(&oFL, buf, byterEreceoved);

		//if (byterEreceoved < BUFFER_SIZE)
			//break;
	}
	oFL.flush();
	oFL.close();
			//cout<<"return value is ::"<<byterEreceoved<<endl;
			//cout<<"Errno value is ::"<<errno<<endl;


			if(errno==0)
				{
					cout<<"Ready message sent"<<endl;
					// SEND_READY and wait to receive SUCCESS
				(void) send(fd,CMD_READY,10,0);

				}
				else
				{
				/*	if(std::remove(completeFilePAth)!=0)
					{

						cout<<"Error deleting file";
					}

					else
					{

					    cout<<"File successfully deleted";
					}*/
				}


			/*if (errno == EINTR)
			 *
					continue;*/
		// SEND_READY and wait to receive SUCCESS
		//(void) send(fd,CMD_READY,10,0);

		char * strSuccess=(char *)malloc(BUFFER_SIZE);
		int successBytes = recv(fd, strSuccess,10,0);


		if(!strcmp(strSuccess,CMD_SUCCESS))
		{
			exit(1);

		}
		else
		{

			//cout<<"Communication not complete"<<endl;
		}


	cout<<"byterEreceoved = "<<byterEreceoved<<endl;
	//cout<<"Recv Return Erron # "<<errno<<endl;
	//printf("Recv Return Error: %s\n", strerror(errno));
	cout<<"Last block reached"<<endl;

	(void) close(fd);

}

//void DeleteFileFolder(int fd, char * loadPath)

void * DeleteFileFolder(int fd)
{

		int	num_read_bytes=-1,num_bytes_fname=-1, status;	// number of bytes received and integer from client
		char 	*fname;		// file name
		char 	*fullFilePath;	// file name
		char 	buf[BUFFER_SIZE];// buffer for storing bytes from file we have to decide dynamic size of this buffer


		//char * cmdACK="cmdreceived";
		//(void) write(fd,cmdACK,strlen(cmdACK));
		//const char* ACK_ACCEPT = "ack_accept";
		//cerr<<"Trig from TCPProcessRequest()"<<endl;

		cerr<<"Send ACK_DELETE command"<<endl;
		//SEND ACK_DELETE
		(void) send(fd,ACK_DELETE,10,0);


		//cout<<"Entered this area"<<endl;
		// Read Comma separated string ( vendor_id,file Path(including file name,chunk id ) from client
		fname = (char *)malloc(FILENAME_SIZE);
		//while( (num_bytes_fname = read(fd, fname, FILENAME_SIZE)) < 0);
		//num_bytes_fname = recv(fd, fname, FILENAME_SIZE,0);

		size_t fileSize=FILENAME_SIZE;
		size_t returnBuffersize=0;
		size_t temppos= returnBuffersize;
		char * ActualBuffer=new char [FILENAME_SIZE];

		while((num_bytes_fname = recv(fd, fname, FILENAME_SIZE,0)) > 0)
			 {
				temppos= returnBuffersize;
				returnBuffersize+=num_bytes_fname;

				ActualBuffer =(char *)realloc(ActualBuffer,returnBuffersize);
				memcpy(ActualBuffer+temppos,fname,num_bytes_fname);
				cout<<"num_bytes_fname = "<<num_bytes_fname<<endl;

				if (num_bytes_fname < FILENAME_SIZE)
					break;
			}



		//zodnum_bytes_fname = read(fd, fname, FILENAME_SIZE);
		if(num_bytes_fname < 0)
		{
			printf("Error reading from client %s %d\n", fname, num_bytes_fname);
			exit(1);
		}
		fname[returnBuffersize] = '\0';

		cerr<<"File Path received from the main server is :"<<fname<<endl;

		if(errno==0)
		{
			cout<<"Ready message sent"<<endl;
		// SEND_READY
		(void) send(fd,CMD_READY,10,0);

		}
		else
		{
			cout<<"Terminating application as File Metadata for DELETING is not received completely"<<endl;

			exit(1);
		}

		cout<<"FPath read is :"<<fname<<endl;
		//vector used to store strings vendor id, filepath and chunk index
		vector<string> vecMetaData;
		const int noMetaData=3;

		//vector used to store directory hierarchy with last index as file name
		vector<string> vecFileHierarchy;

		if(fname!=NULL)
		{
			std::string str(fname);
			split(str,',',vecMetaData);
		}

		int lenVecMetaData=vecMetaData.size();
		int	lenVecHierarchy=0;

		string strFileName;
		string vendorID;
		int iChunkIndex=-1;
		//If the data read includes vendorid, filepath and chunk index
		bool bCreateFolderStructure = true;
		if(lenVecMetaData==noMetaData)
		{


			string strLoadPAth(loadPAth);
			strLoadPAth.append("/");
			strLoadPAth.append(vecMetaData[0]);
			//strLoadPAth.append("/");
			strLoadPAth.append(vecMetaData[1]);
			strLoadPAth.append("_");
			strLoadPAth.append(vecMetaData[2]);

			cerr<<"Trying to delete file : "<<strLoadPAth<<endl;
			//Check if the file path exist and if so delete the file
			struct stat st;
			if(stat(strLoadPAth.c_str(),&st) < 0)
			{

			 cerr<<"File does not exiztdoes not exist";
			 }
			else
			{
			  int delREsult= remove(strLoadPAth.c_str());

			  if(delREsult==0)
			  cerr<<"File exists and is deleted"<<endl;
			  else
			  cerr<<"File exists ,NUT unableTO DELETE FOLDER"<<endl;


			}

		}
		else
		{

			cerr<<"Unable to extract vendor ID, file name and chunk index from the edge server"<<endl;
		}

		(void) close(fd);
}





























